//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using OpenLS.Drawing;
using OpenLS.Media;
using OpenLS.Spreadsheet.Drawing;
using OpenLS.Spreadsheet.UI.Internals;
using OpenLS.Spreadsheet.UI.Internals.Resources;
using OpenLS.UI;

namespace OpenLS.Spreadsheet.UI
{
    static class GridClipboard
    {
        public static void EnableCommands(UIElement g)
        {
            g.CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut,onCut, WorksheetHelper.OnHasSelection));
            g.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, onCopy, WorksheetHelper.OnHasSelection));
            GridCommandHelper.AddWorksheetCommandBinding(g, ClipboardCommands.FormatPainter, onFormatPainter, WorksheetHelper.OnHasSelection);
            //g.CommandBindings.Add(new CommandBinding(ClipboardCommands.FormatPainter,onFormatPainter, WorksheetHelper.ProtectionFunction(PasteProtection)));
            AddPasteCommandBinding(g, ApplicationCommands.Paste, onPaste);
            AddPasteCommandBinding(g, ClipboardCommands.PasteValues,
                onPasteValues);
            AddPasteCommandBinding(g, ClipboardCommands.PasteFormulas,
                onPasteFormulas);
            AddPasteCommandBinding(g, ClipboardCommands.PasteNoBorders,
                onPasteNoBorders);
            AddPasteCommandBinding(g, ClipboardCommands.PasteSpecial,
                onPasteSpecial);
            AddPasteCommandBinding(g, ClipboardCommands.PasteTranspose,
                onPasteTranspose);
        }
        static void AddPasteCommandBinding (UIElement g, RoutedCommand command, GridCommandHelper.MultiAction<object, Worksheet, object> action)
        {
            Func<Worksheet, bool> canpaste = (ws) => 
                ws.Selection != null && (!ws.Selection.IsLocked.GetValueOrDefault(true)) || WorksheetHelper.DefaultProtectionFunction(ws) ;
            GridCommandHelper.AddWorksheetCommandBinding(g, command, safeAction(action), canpaste);
        }

        private static GridCommandHelper.MultiAction<object, Worksheet, object> safeAction(GridCommandHelper.MultiAction<object, Worksheet, object> action)
        {
            return delegate(object o, Worksheet w, object o2)
                       {
                           try
                           {
                               action(o, w, o2);
                           }
                           catch (Exception e)
                           {
                               MessageBox.Show("Could not copy the table");
                               // Contract.Assert(false, e.ToString());
                           }
                       };
        }

        internal static void PasteFormats(UIElement g)
        {
            onPasteWithFunction(g, new PasteSpecialOperation(RangeFunctions.ApplyFormats));
        }
        static void onCut(object sender, ExecutedRoutedEventArgs args)
        {

            try{
            Worksheet worksheet = getWorksheet(sender);
            if (worksheet.Selection == null)
                return;
            SpreadsheetElement.SetHighlightFor(sender, worksheet.Selection);
            ClipboardManager.Cut(worksheet.Selection);
                ClipboardMonitor.EnsureMonitored((UIElement) sender);
                        }
            catch (Exception e)
            {
                // Contract.Assert(false);
            }

        }
        static void onCopy(object sender, ExecutedRoutedEventArgs args)
        {
            try
            {
#if DEBUG
            try
            {
#endif
                Worksheet worksheet = getWorksheet(sender);
                if (worksheet.Selection == null)
                    return;
                ClipboardManager.Copy(worksheet.Selection);
                SpreadsheetElement.SetHighlightFor(sender, worksheet.Selection);
                ClipboardMonitor.EnsureMonitored((UIElement)sender);
#if DEBUG
            }catch (Exception ee)
            {
                Debug.WriteLine ("Failed copy" + ee);
            }
#endif
            }
            catch (Exception e)
            {
                // Contract.Assert(false);
            }

        }
        static void onFormatPainter(object sender, Worksheet worksheet, object parameter)
        {
            if (worksheet.Selection == null)
                return;
            SpreadsheetElement.SetHighlightFor(sender, worksheet.Selection);
            ((FrameworkElement)sender).Cursor = OfficeCursors.PaintCell;
            ClipboardManager.Copy(worksheet.Selection);
        }
        static Worksheet getWorksheet(object sender)
        {
            return WorksheetHelper.GetWorksheet(sender);
        }
        static void onPaste(object sender, Worksheet worksheet, object parameter)
        {
                using (worksheet.CreateUndoScope(ApplicationCommands.Paste.Name))
                {
                    onPasteWithFunction(sender,
                                        new PasteSpecialOperation(RangeFunctions.ApplyValueAndFormulaAndStyleRecord));
                    Range pastedRange = ClipboardManager.CopiedRange();
                    if (pastedRange != null)
                    {
                        //RangeFunctions.ApplyAll.TableFunction.CopyFunction(pastedRange, worksheet.Selection);
                        CellRelativeAddress offset = worksheet.Selection.TopLeftCell.Address -
                                                     pastedRange.Worksheet.Selection.TopLeftCell.Address;
                        foreach (var table in pastedRange.Worksheet.Tables)
                        {
                            Range r = table.SimpleRange.ProjectedSimple(worksheet, offset);
                            List<Table> tables =
                                new List<Table>(worksheet.Tables).FindAll(t => Range.Intersects(t.Range, r));
                            if (tables.Count == 0)
                                worksheet.AddTable(r, table.Style,table.Name+"_bis", table.Comment, table.IsPublic, table.IsReadOnly, table.AllowsUpdate, table.AllowsDelete, table.AllowsInsert);
                        }
                    }
                }
        }
        static void onPasteWithFunction(object sender, PasteSpecialOperation fns)
        {
            Worksheet worksheet = getWorksheet(sender);
            Range pastedRange = ClipboardManager.CopiedRange();
            if (pastedRange == null)
            {
                string s = Clipboard.GetText(TextDataFormat.Text);
                if (string.IsNullOrEmpty(s))
                {
                    onPasteImage(worksheet);
                    return;
                }
                var lines = new List<List<string>>();
                s = s.TrimEnd();
                foreach (IEnumerable<string> l in SimpleTextParser.Parse(s))
                {
                    var ll = new List<string>();
                    ll.AddRange(l);
                    lines.Add(ll);
                }
                //\\lines.AddRange(SimpleTextParser.Parse(s));
                if (lines.Count == 1 && lines[0].Count == 1 && worksheet.Selection != null)
                {
                    if (worksheet.Selection is CellsRange)
                        foreach (Cell c1 in worksheet.Selection.GetAllCells())
                            c1.LocalizedFormula = lines[0][0];
                    if (worksheet.Selection is FullRange)
                        worksheet.Selection.FirstCell.LocalizedFormula = lines[0][0];
                    SpreadsheetElement.ForgetHightlightForAndClipboardMonitor(sender);
                    return;
                }
                Cell c = worksheet.ActiveCell;
                foreach (IEnumerable<string> line in SimpleTextParser.Parse(s))
                {
                    Cell a = c;
                    foreach (string ss in line)
                    {
                        try
                        {
                            a.SetFormattingRecordSafe(null);
                            a.LocalizedFormula = ss;
                        }
                        catch { }
                        a = a.GetNeighbor(NavigationDirection.Right);
                    }
                    c = c.GetNeighbor(NavigationDirection.Down);
                }
                return;
            }

            Range highlight = SpreadsheetElement.GetHighlight((DependencyObject)sender);
            if (true)
            {
                var range = pastedRange;
                if (fns.Transpose)
                    range = TransposeRange(range);
                var nextSelection = (range).Projected(worksheet, worksheet.Selection.TopLeftCell.Address - pastedRange.TopLeftCell.Address);
                Range pasteTarget;
                try
                {
                    pasteTarget = PasteManager.GetPasteTarget(pastedRange, worksheet.Selection);
                }
                catch (ApplicationException)
                {
                    MessageBox.Show(UIStringTable.InvalidPasteShape, "");
                    return;
                    
                }
                Contract.Assert(nextSelection.Worksheet == worksheet);
                Contract.Assert(pasteTarget.Worksheet== worksheet);
                if (NeedsPasteWarning(pasteTarget,
                    nextSelection))
                    if (MessageBox.Show(UIStringTable.PasteOverflow, "", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
                        return;
                if (ClipboardManager.LastWasCut)
                {
                    if (highlight != null)
                    {
                        foreach (Cell cell in highlight.GetNonEmptyCells())
                            fns.RangeFunction.ClearCell(cell);
                        fns.RangeFunction.TableFunction.ClearFunction(highlight);
                    }
                }
                worksheet.Selection = nextSelection;
                worksheet.Paste(pastedRange, new PasteInfo(fns.RangeFunction, fns.Transpose, ClipboardManager.LastWasCut));
            }
            if (highlight != null)
            {
                if (highlight is CellsRange)
                {

                    var r = (CellsRange)highlight;
                    if (!r.IsSingleCell)
                    {
                        worksheet.Selection = fns.GetTargetRange(r, worksheet);
                    }
                }
                if (ClipboardManager.LastWasCut)
                    SpreadsheetElement.ForgetHightlightForAndClipboardMonitor(sender);
            }
        }

        private static bool NeedsPasteWarning(Range selection, Range affectedRange)
        {
            Contract.Requires(selection != null);
            Contract.Requires(affectedRange != null);
            Contract.Requires(affectedRange.Worksheet == selection.Worksheet);
            if (selection.Surface == 1 || affectedRange.Surface == 1)
                return false;
            if (selection.RowSpan == affectedRange.RowSpan && selection.ColumnSpan == affectedRange.RowSpan)
                return false;
            var selectionCells = selection.GetNonEmptyCells();
            var affectedCells = affectedRange.GetNonEmptyCells();
            selectionCells = Enumerable.Where(selectionCells, c => c.Value != null || c.Formula != null);
            affectedCells = Enumerable.Where(affectedCells, c => c.Value != null || c.Formula != null);
            var dic = Enumerable.ToDictionary(selectionCells, c => c);
            affectedCells = Enumerable.Where(affectedCells, c => !dic.ContainsKey(c));
            return Enumerable.Any(affectedCells, c => true);
        }

        private static Range TransposeRange(Range range)
        {
            if (range is SingleCellRange)
                return range;
            if (range is CellsRange)
                return CellsRange.Create(range.Worksheet,
                                         new RangeAddress(range.Address.TopLeft,
                                                          range.Address.TopLeft +
                                                          new CellRelativeAddress(range.Address.ColumnSpan - 1,
                                                                                  range.Address.RowSpan - 1)));
            throw new NotSupportedException();
        }

        private static void onPasteImage(Worksheet worksheet)
        {
            Debug.WriteLine("Trying to paste image");
            ImageSource source = Clipboard.GetImage();
            if (source == null)
                return;
            BitmapSource cleanSource = ImageSourceHelper.CleanImageSource(source);
            var pic = new Picture(cleanSource);
            var anchor = new OneCellAnchor(worksheet) {Content = pic, Width = pic.DesiredWidth, Height = pic.DesiredHeight};
            worksheet.Anchors.Add(anchor);
        }
        
        static void onPasteSpecial(object sender, Worksheet workssheet, object parameter)
        {
            var w = new ContentControl();
            var op = new PasteSpecialOperation();
            w.Content = op;
            w.Resources.MergedDictionaries.Add(new PasteSpecialOperationResources());

            WindowServices.ShowDialog(w, ClipboardCommands.PasteSpecial.Text, sender as UIElement,
                                      obj => onPasteWithFunction(sender, op), o => false);
        }
        static void onPasteTranspose(object sender, Worksheet workssheet, object parameter)
        {
            onPasteWithFunction(sender, new PasteSpecialOperation(RangeFunctions.ApplyAllAndFormula){Transpose = true});
          //  onPasteWithFunction(sender, new PasteSpecialOperation(RangeFunctions.ApplyAll){Transpose =  true});
        }
        //static void onPasteTransposeold(object sender, ExecutedRoutedEventArgs args) //\\
        //{
        //    Worksheet worksheet = getWorksheet(sender);
        //    Range pastedRange = ClipboardManager.CopiedRange();
        //    if (pastedRange == null)
        //    {
        //        string s = Clipboard.GetText(TextDataFormat.Text);
        //        if (s == null)
        //            return;
        //        worksheet.ActiveCell.LocalizedFormula = s;
        //        return;
        //    }

        //    {
        //        var r = pastedRange;
        //        if (ClipboardManager.LastWasCut)
        //            worksheet.MoveRange(r, worksheet.ActiveCell.Address);
        //        else
        //            worksheet.CopyRange(r, worksheet.ActiveCell.Address, 
        //                                RangeFunctions.ApplyvalueAndStyleRecord.CopyFunction);
        //    }
        //    Range hightlight = SpreadsheetElement.GetHighlight((DependencyObject)sender);
        //    if (hightlight != null)
        //    {
        //        if (hightlight is CellsRange)
        //        {
        //            var r = (CellsRange)hightlight;
        //            worksheet.Selection = CellsRange.Create(r.Worksheet, new RangeAddress(worksheet.ActiveCell.Address,
        //                worksheet.ActiveCell.Address + r.Address.Offset));
        //        }
        //        if (ClipboardManager.LastWasCut)
        //            SpreadsheetElement.SetHighlightFor(sender, null);// Worksheet.Highlight = null;
        //    }
        //}
        static void onPasteValues(object sender2, Worksheet workssheet, object parameter)
        {
            onPasteWithFunction(sender2, new PasteSpecialOperation(OpenLS.Spreadsheet.RangeFunctions.ApplyValue));

        }
        static void onPasteFormulas(object sender, Worksheet workssheet, object parameter)
        {
            onPasteWithFunction(sender, new PasteSpecialOperation(RangeFunctions.ApplyFormula));
        }
        static void onPasteNoBorders(object sender, Worksheet workssheet, object parameter)
        {
            onPasteWithFunction(sender, new PasteSpecialOperation(RangeFunctions.ApplyAllExceptBorders));
        }
    }
}
